﻿using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.Events;
using Anderson.PluginFramework;
using Anderson.Utils;

namespace Anderson.UnityFramework
{
    public abstract class BaseFramework : MonoBehaviour, IFramework
    {
        #region IFramework.Properties
        Dictionary<long, IPlugin> dictPluginById = new Dictionary<long, IPlugin>();

        Transform m_pluginRootTransform;
        IList<Type> m_commonServices;
        IList<PluginEntity> m_rootEntities;

        bool m_autoStartBeforeLoaded;
        #endregion

        #region IPlugin.Properties
        ILog m_log;
        PluginEntity m_PluginEntity;
        IPluginActivator m_activatorInstance = null;
        bool m_resolved;
        bool m_started;
        #endregion

        #region IContextListener.Properties
        PluginEvent PluginChangedHandler = new PluginEvent();
        ServiceEvent ServiceChangedHandler = new ServiceEvent();
        FrameworkEvent FrameworkChangedHandler = new FrameworkEvent();
        #endregion

        #region IServiceBus.Properties
        IDictionary<string, ServiceReference> m_dictAllService = new Dictionary<string, ServiceReference>();
        #endregion

        #region IFramework
        public IList<IPlugin> AllPlugin
        {
            get
            {
                IList<IPlugin> list = new List<IPlugin>();

                foreach (IPlugin item in dictPluginById.Values)
                {
                    list.Add(item);
                }

                return list;
            }
        }

        public Transform PluginRootTransform
        {
            get
            {
                return m_pluginRootTransform;
            }

            set
            {
                m_pluginRootTransform = value;
            }
        }

        public bool AutoStartBeforeLoaded
        {
            get
            {
                return m_autoStartBeforeLoaded;
            }

            set
            {
                m_autoStartBeforeLoaded = value;
            }
        }

        public void InstallPlugin(PluginEntity entity)
        {
            StartCoroutine(AsyncInstallPlugin(entity));
        }

        public IEnumerator AsyncInstallPlugin(PluginEntity entity)
        {
            m_log.Debug(string.Format("下载并安装插件:{0} - {1}", entity.pluginName, entity.assetPathOrUrl));

            var uwr = UnityWebRequest.GetAssetBundle(entity.assetPathOrUrl);
            yield return uwr.SendWebRequest();

            AssetBundle ab = DownloadHandlerAssetBundle.GetContent(uwr);
            if (null == ab)
            {
                m_log.Error(string.Format("插件下载失败:{0} - {1}", entity.pluginName, entity.assetPathOrUrl));
                yield break;
            }

            TextAsset asset = ab.LoadAsset<TextAsset>(entity.projectPath);
            if (null == asset)
            {
                m_log.Error(string.Format("插件内容无效:{0}", entity.pluginName));
                yield break;
            }

            entity.assemblyAsset = asset;

            IPlugin plugin = new Plugin(this, m_log, entity);
            dictPluginById.Add(entity.id, plugin);

            plugin.Load();

            if (null != ab)
            {
                ab.Unload(false);
            }
        }

        public void UninstallPlugin(IPlugin plugin)
        {
            dictPluginById.Remove(plugin.Entity.id);

            plugin.Uninstall();
        }

        public IList<Type> CommonServices
        {
            set
            {
                m_commonServices = value;
            }
        }

        public IList<PluginEntity> RootEntities
        {
            set
            {
                m_rootEntities = value;
            }
        }
        #endregion

        #region IPlugin

        public IPluginActivator ActivatorInstance
        {
            get
            {
                return m_activatorInstance;
            }
        }

        public IFramework Framework
        {
            get
            {
                return this;
            }
            set
            {
                //Do Nothing
                //自己就是Framework
                //作为特殊的IPlugin不需要修改该属性
            }
        }

        public ILog Log
        {
            get
            {
                return m_log;
            }
            set
            {
                m_log = value;
            }
        }

        public bool IsGameObjectActivator
        {
            get
            {
                return true;
            }
        }

        public PluginEntity Entity
        {
            get
            {
                return m_PluginEntity;
            }
            set
            {
                m_PluginEntity = value;
            }
        }

        public bool Resolved
        {
            get
            {
                return m_resolved;
            }

            set
            {
                m_resolved = value;
            }
        }

        public bool Started
        {
            get
            {
                return m_started;
            }

            set
            {
                m_started = value;
            }
        }

        public void Load()
        {
            //1. 注册标注服务
            //2. 在在指定位置加载插件（可以是数据库，可以是本地目录）(外部注入)
            //3. 循环处理需要加载的插件
            //4. 触发事件

            //2018.05.05
            //Load可能被多次执行

            if (!m_resolved)
            {
                RegisterCommonService();
                StartCoroutine(InstallRootPlugins());
            }
        }

        public void Run()
        {
            if (m_resolved && !m_started)
            {
                RunAllPlugins();
            }
        }

        public void Stop()
        {
            if (m_resolved && m_started)
            {
                foreach (long id in dictPluginById.Keys)
                {
                    dictPluginById[id].Stop();
                }

                m_started = false;
            }
        }

        public void Uninstall()
        {
            if (m_resolved)
            {
                foreach (long id in dictPluginById.Keys)
                {                    
                    dictPluginById[id].Uninstall();
                }
                dictPluginById.Clear();

                UnRegisterCommonService();

                m_resolved = false;
            }
        }
        #endregion

        #region IContextListener
        public void AddPluginListener(UnityAction<PluginEventArg> call)
        {
            PluginChangedHandler.AddListener(call);
        }

        public void AddFrameworkListener(UnityAction<FrameworkEventArg> call)
        {
            FrameworkChangedHandler.AddListener(call);
        }

        public void AddServiceListener(UnityAction<ServiceEventArg> call)
        {
            ServiceChangedHandler.AddListener(call);
        }

        public void FirePluginEvent(PluginEventArg arg)
        {
            PluginChangedHandler.Invoke(arg);
        }

        public void FireFrameworkEvent(FrameworkEventArg arg)
        {
            FrameworkChangedHandler.Invoke(arg);
        }

        public void FireServiceEvent(ServiceEventArg arg)
        {
            ServiceChangedHandler.Invoke(arg);
        }

        public void RemovePluginListener(UnityAction<PluginEventArg> call)
        {
            PluginChangedHandler.RemoveListener(call);
        }

        public void RemoveFrameworkListener(UnityAction<FrameworkEventArg> call)
        {
            FrameworkChangedHandler.RemoveListener(call);
        }

        public void RemoveServiceListener(UnityAction<ServiceEventArg> call)
        {
            ServiceChangedHandler.RemoveListener(call);
        }
        #endregion

        #region IServiceBus
        public IDictionary<string, ServiceReference> DictAllService
        {
            get
            {
                return m_dictAllService;
            }
        }

        public object GetService(IPlugin leaseholder, Type serviceType, bool createNewInstance)
        {
            string typeName = serviceType.FullName;
            if (m_dictAllService.ContainsKey(typeName))
            {
                m_log.Warn(string.Format("获取不存在的服务，类型为{0}。", serviceType));
                return null;
            }

            ServiceReference serviceRef = m_dictAllService[typeName];

            if (!createNewInstance)
            {
                return serviceRef.serviceInstance;
            }

            return Activator.CreateInstance(serviceRef.serviceType);
        }

        public void RegisterService(IPlugin owner, Type serviceType, object serviceInstance)
        {
            string typeName = serviceType.FullName;
            if (m_dictAllService.ContainsKey(typeName))
            {
                m_log.Warn(string.Format("已经存在类型为{0}的服务。", serviceType.FullName));
                return;
            }

            m_log.Debug(string.Format("注册类型为{0}的服务。", serviceType.FullName));

            ServiceReference serviceRef = new ServiceReference();
            serviceRef.owner = owner;
            serviceRef.serviceType = serviceType;
            serviceRef.serviceInstance = serviceInstance;
            m_dictAllService.Add(typeName, serviceRef);

            FireServiceEvent(new ServiceEventArg(ServiceEventType.Register, owner));
        }

        public void UnRegisterService(IPlugin owner, Type serviceType, object serviceInstance)
        {
            string typeName = serviceType.FullName;
            if (!m_dictAllService.ContainsKey(typeName))
            {
                m_log.Warn(string.Format("注销不存在的服务，类型为{0}。", serviceType));
                return;
            }

            ServiceReference serviceRef = m_dictAllService[typeName];

            m_log.Debug(string.Format("取消注册类型为{0}的服务。", serviceType.FullName));

            FireServiceEvent(new ServiceEventArg(ServiceEventType.UnRegister, owner));

            serviceRef.serviceInstance = null;
            m_dictAllService.Remove(typeName);
        }

        public void UnGetService(IPlugin leaseholder, Type serviceType)
        {
            throw new NotImplementedException();
        }
        #endregion

        #region IPlugin Inner
        private void UnRegisterCommonService()
        {
            if (null == m_commonServices || 0 == m_commonServices.Count)
            {
                m_log.Info("没有需要取消注册的标注服务。");
                return;
            }

            Type serviceType;
            int count = m_commonServices.Count;
            for (int i = 0; i < count; i++)
            {
                serviceType = m_commonServices[i];
                UnRegisterService(this, serviceType, Activator.CreateInstance(serviceType));
            }
        }

        private void RegisterCommonService()
        {
            //怎么知道需要注册哪些服务？
            //应该是FrameworkFactory通知，需要有个参数接收
            if (null == m_commonServices || 0 == m_commonServices.Count)
            {
                m_log.Info("没有需要注册的标注服务。");
                return;
            }

            Type serviceType;
            int count = m_commonServices.Count;
            for (int i = 0; i < count; i++)
            {
                serviceType = m_commonServices[i];
                RegisterService(this, serviceType, Activator.CreateInstance(serviceType));
            }
        }

        IEnumerator InstallRootPlugins()
        {
            if (null == m_rootEntities || 0 == m_rootEntities.Count)
            {
                m_log.Info("没有需要安装的基础插件。");
                yield return null;
            }

            int count = m_rootEntities.Count;
            //for (int i = 0; i < count; i++)
            //{
            //    StartCoroutine(AsyncInstallPlugin(m_rootEntities[i]));
            //}

            yield return DoInstallPluginsOneByOne(m_rootEntities, 0, count);
        }

        IEnumerator DoInstallPluginsOneByOne(IList<PluginEntity> list, int index, int count)
        {
            if (index > count - 1)
            {
                AllPluginLoaded();
                yield break;
            }

            yield return StartCoroutine(AsyncInstallPlugin(m_rootEntities[index]));

            index++;

            yield return DoInstallPluginsOneByOne(list, index, count);
        }

        void AllPluginLoaded()
        {
            m_resolved = true;

            FrameworkEventArg arg = new FrameworkEventArg(FrameworkEventType.AllPluginsLoaded);
            FireFrameworkEvent(arg);

            if (m_autoStartBeforeLoaded)
            {
                Run();
            }
        }

        void RunAllPlugins()
        {
            foreach (long id in dictPluginById.Keys)
            {
                dictPluginById[id].Run();
            }

            m_started = true;
        }
        #endregion

    }
}
